Раскройте возможности федерации GraphQL с помощью сшивания схем. Узнайте, как создать единый GraphQL API из нескольких сервисов, улучшая масштабируемость и поддерживаемость.
Федерация GraphQL: Сшивание схем (Schema Stitching) — подробное руководство
В постоянно меняющемся ландшафте современной разработки приложений необходимость в масштабируемых и поддерживаемых архитектурах стала первостепенной. Микросервисы, с их присущей модульностью и независимостью развертывания, стали популярным решением. Однако управление множеством микросервисов может привести к сложностям, особенно когда речь идет о предоставлении единого API для клиентских приложений. Именно здесь на помощь приходит федерация GraphQL, а в частности — сшивание схем (Schema Stitching).
Что такое федерация GraphQL?
Федерация GraphQL — это мощная архитектура, которая позволяет создать единый, унифицированный API GraphQL из нескольких нижележащих сервисов GraphQL (часто представляющих собой микросервисы). Она позволяет разработчикам запрашивать данные из разных сервисов так, как если бы это был один граф, упрощая работу на стороне клиента и снижая потребность в сложной логике оркестрации.
Существует два основных подхода к федерации GraphQL:
- Сшивание схем (Schema Stitching): Этот подход включает объединение нескольких схем GraphQL в одну, унифицированную схему на уровне шлюза. Это более ранний подход, который полагается на библиотеки для управления объединением схем и делегированием запросов.
- Apollo Federation: Это более новый и надежный подход, который использует декларативный язык схем и специальный планировщик запросов для управления процессом федерации. Он предлагает расширенные функции, такие как расширения типов, ключевые директивы и распределенная трассировка.
Эта статья посвящена сшиванию схем (Schema Stitching), в ней рассматриваются его концепции, преимущества, ограничения и практическая реализация.
Понимание сшивания схем
Сшивание схем — это процесс объединения нескольких схем GraphQL в единую, целостную схему. Эта унифицированная схема действует как фасад, скрывая сложность нижележащих сервисов от клиента. Когда клиент отправляет запрос к сшитой схеме, шлюз интеллектуально направляет запрос к соответствующему сервису (или сервисам), извлекает данные и объединяет результаты перед их возвращением клиенту.
Представьте это так: у вас есть несколько ресторанов (сервисов), каждый из которых специализируется на разных кухнях. Сшивание схем — это как универсальное меню, которое объединяет все блюда из каждого ресторана. Когда клиент заказывает из универсального меню, заказ интеллектуально направляется на соответствующие кухни ресторанов, еда готовится, а затем объединяется в одну доставку для клиента.
Ключевые концепции сшивания схем
- Удаленные схемы: Это индивидуальные схемы GraphQL каждого нижележащего сервиса. Каждый сервис предоставляет свою собственную схему, которая определяет данные и операции, которые он предоставляет.
- Шлюз (Gateway): Шлюз — это центральный компонент, отвечающий за сшивание удаленных схем и предоставление унифицированной схемы клиенту. Он получает клиентские запросы, направляет их в соответствующие сервисы и объединяет результаты.
- Слияние схем (Schema Merging): Это процесс объединения удаленных схем в единую схему. Часто это включает переименование типов и полей для избежания конфликтов и определение отношений между типами из разных схем.
- Делегирование запросов (Query Delegation): Когда клиент делает запрос к сшитой схеме, шлюзу необходимо делегировать запрос соответствующему нижележащему сервису (или сервисам) для получения данных. Это включает преобразование запроса клиента в запрос, понятный удаленному сервису.
- Агрегация результатов (Result Aggregation): После того как шлюз получил данные от нижележащих сервисов, ему необходимо объединить результаты в единый ответ, который можно вернуть клиенту. Это часто включает преобразование данных для соответствия структуре сшитой схемы.
Преимущества сшивания схем
Сшивание схем предлагает несколько весомых преимуществ для организаций, внедряющих микросервисную архитектуру:
- Единый API: Предоставляет единый, последовательный API для клиентов, упрощая доступ к данным и избавляя клиентов от необходимости напрямую взаимодействовать с несколькими сервисами. Это приводит к более чистому и интуитивно понятному опыту для разработчиков.
- Снижение сложности на стороне клиента: Клиентам нужно взаимодействовать только с унифицированной схемой, что защищает их от сложностей нижележащей микросервисной архитектуры. Это упрощает разработку на стороне клиента и уменьшает количество необходимого кода.
- Повышенная масштабируемость: Позволяет масштабировать отдельные сервисы независимо в зависимости от их конкретных потребностей. Это улучшает общую масштабируемость и отказоустойчивость системы. Например, сервис пользователей, испытывающий высокую нагрузку, можно масштабировать, не затрагивая другие сервисы, такие как каталог продуктов.
- Улучшенная поддерживаемость: Способствует модульности и разделению ответственности, что облегчает поддержку и развитие отдельных сервисов. Изменения в одном сервисе с меньшей вероятностью повлияют на другие.
- Постепенное внедрение: Может быть реализовано постепенно, что позволяет плавно переходить от монолитной архитектуры к микросервисной. Вы можете начать со сшивания существующих API, а затем постепенно разделять монолит на более мелкие сервисы.
Ограничения сшивания схем
Хотя сшивание схем предлагает множество преимуществ, важно осознавать его ограничения:
- Сложность: Внедрение и управление сшиванием схем может быть сложным, особенно в больших и сложных системах. Необходимо тщательное планирование и проектирование.
- Накладные расходы на производительность: Шлюз вводит некоторые накладные расходы на производительность из-за дополнительного уровня косвенности и необходимости делегировать запросы и агрегировать результаты. Тщательная оптимизация имеет решающее значение для минимизации этих расходов.
- Конфликты схем: При слиянии схем из разных сервисов могут возникать конфликты, особенно если они используют одинаковые имена типов или полей. Это требует тщательного проектирования схем и, возможно, переименования типов и полей.
- Ограниченные расширенные функции: По сравнению с Apollo Federation, сшиванию схем не хватает некоторых расширенных функций, таких как расширения типов и ключевые директивы, что может усложнить управление отношениями между типами из разных схем.
- Зрелость инструментария: Инструментарий и экосистема вокруг сшивания схем не так зрелы, как у Apollo Federation. Это может затруднить отладку и устранение проблем.
Практическая реализация сшивания схем
Давайте рассмотрим упрощенный пример реализации сшивания схем с использованием Node.js и библиотеки graphql-tools
(популярный выбор для сшивания схем). Этот пример включает два микросервиса: сервис пользователей (User Service) и сервис продуктов (Product Service).
1. Определение удаленных схем
Сначала определим схемы GraphQL для каждого из удаленных сервисов.
Сервис пользователей (user-service.js
):
const { buildSchema } = require('graphql');
const userSchema = buildSchema(`
type User {
id: ID!
name: String
email: String
}
type Query {
user(id: ID!): User
}
`);
const users = [
{ id: '1', name: 'Alice Smith', email: 'alice@example.com' },
{ id: '2', name: 'Bob Johnson', email: 'bob@example.com' },
];
const userRoot = {
user: (args) => users.find(user => user.id === args.id),
};
module.exports = {
schema: userSchema,
rootValue: userRoot,
};
Сервис продуктов (product-service.js
):
const { buildSchema } = require('graphql');
const productSchema = buildSchema(`
type Product {
id: ID!
name: String
price: Float
userId: ID! # Внешний ключ к User Service
}
type Query {
product(id: ID!): Product
}
`);
const products = [
{ id: '101', name: 'Laptop', price: 1200, userId: '1' },
{ id: '102', name: 'Smartphone', price: 800, userId: '2' },
];
const productRoot = {
product: (args) => products.find(product => product.id === args.id),
};
module.exports = {
schema: productSchema,
rootValue: productRoot,
};
2. Создание сервиса-шлюза
Теперь создадим сервис-шлюз, который будет сшивать две схемы вместе.
Сервис-шлюз (gateway.js
):
const { stitchSchemas } = require('@graphql-tools/stitch');
const { makeRemoteExecutableSchema } = require('@graphql-tools/wrap');
const { graphqlHTTP } = require('express-graphql');
const express = require('express');
const { introspectSchema } = require('@graphql-tools/wrap');
const { printSchema } = require('graphql');
const fetch = require('node-fetch');
async function createRemoteSchema(uri) {
const fetcher = async (params) => {
const response = await fetch(uri, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(params),
});
return response.json();
};
const schema = await introspectSchema(fetcher);
return makeRemoteExecutableSchema({
schema,
fetcher,
});
}
async function main() {
const userSchema = await createRemoteSchema('http://localhost:4001/graphql');
const productSchema = await createRemoteSchema('http://localhost:4002/graphql');
const stitchedSchema = stitchSchemas({
subschemas: [
{ schema: userSchema },
{ schema: productSchema },
],
typeDefs: `
extend type Product {
user: User
}
`,
resolvers: {
Product: {
user: {
selectionSet: `{ userId }`,
resolve(product, args, context, info) {
return info.mergeInfo.delegateToSchema({
schema: userSchema,
operation: 'query',
fieldName: 'user',
args: {
id: product.userId,
},
context,
info,
});
},
},
},
},
});
const app = express();
app.use('/graphql', graphqlHTTP({
schema: stitchedSchema,
graphiql: true,
}));
app.listen(4000, () => console.log('Gateway server running on http://localhost:4000/graphql'));
}
main().catch(console.error);
3. Запуск сервисов
Вам нужно будет запустить сервис пользователей и сервис продуктов на разных портах. Например:
Сервис пользователей (порт 4001):
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./user-service');
const app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: rootValue,
graphiql: true,
}));
app.listen(4001, () => console.log('User service running on http://localhost:4001/graphql'));
Сервис продуктов (порт 4002):
const express = require('express');
const { graphqlHTTP } = require('express-graphql');
const { schema, rootValue } = require('./product-service');
const app = express();
app.use('/graphql', graphqlHTTP({
schema: schema,
rootValue: rootValue,
graphiql: true,
}));
app.listen(4002, () => console.log('Product service running on http://localhost:4002/graphql'));
4. Запрос к сшитой схеме
Теперь вы можете делать запросы к сшитой схеме через шлюз (работающий на порту 4000). Вы можете выполнить такой запрос:
query {
product(id: "101") {
id
name
price
user {
id
name
email
}
}
}
Этот запрос извлекает продукт с ID "101", а также получает связанного пользователя из сервиса пользователей, демонстрируя, как сшивание схем позволяет запрашивать данные из нескольких сервисов в одном запросе.
Продвинутые техники сшивания схем
Помимо базового примера, существуют некоторые продвинутые техники, которые можно использовать для улучшения вашей реализации сшивания схем:
- Делегирование схемы: Это позволяет делегировать части запроса различным сервисам в зависимости от запрашиваемых данных. Например, вы можете делегировать разрешение типа `User` сервису пользователей, а разрешение типа `Product` — сервису продуктов.
- Трансформация схемы: Это включает в себя изменение схемы удаленного сервиса перед тем, как она будет сшита в единую схему. Это может быть полезно для переименования типов и полей, добавления новых полей или удаления существующих.
- Пользовательские резолверы: Вы можете определять пользовательские резолверы в шлюзе для обработки сложных преобразований данных или для извлечения данных из нескольких сервисов и их объединения в единый результат.
- Обмен контекстом: Часто необходимо обмениваться информацией о контексте между шлюзом и удаленными сервисами, например, токенами аутентификации или идентификаторами пользователей. Этого можно достичь, передавая информацию о контексте в процессе делегирования запроса.
- Обработка ошибок: Реализуйте надежную обработку ошибок для корректного управления ошибками, возникающими в удаленных сервисах. Это может включать логирование ошибок, возврат дружественных пользователю сообщений об ошибках или повторные попытки неудачных запросов.
Выбор между сшиванием схем и Apollo Federation
Хотя сшивание схем является жизнеспособным вариантом для федерации GraphQL, Apollo Federation стала более популярным выбором благодаря своим расширенным функциям и улучшенному опыту разработчиков. Вот сравнение двух подходов:
Функция | Сшивание схем | Apollo Federation |
---|---|---|
Определение схемы | Использует существующий язык схем GraphQL | Использует декларативный язык схем с директивами |
Планирование запросов | Требует ручного делегирования запросов | Автоматическое планирование запросов шлюзом Apollo |
Расширения типов | Ограниченная поддержка | Встроенная поддержка расширений типов |
Ключевые директивы | Не поддерживается | Использует директиву @key для идентификации сущностей |
Распределенная трассировка | Требует ручной реализации | Встроенная поддержка распределенной трассировки |
Инструментарий и экосистема | Менее зрелый инструментарий | Более зрелый инструментарий и большое сообщество |
Сложность | Может быть сложным в управлении в больших системах | Разработано для больших и сложных систем |
Когда выбирать сшивание схем:
- У вас есть существующие сервисы GraphQL, и вы хотите быстро их объединить.
- Вам нужно простое решение для федерации и не требуются расширенные функции.
- У вас ограниченные ресурсы, и вы хотите избежать накладных расходов на настройку Apollo Federation.
Когда выбирать Apollo Federation:
- Вы создаете большую и сложную систему с несколькими командами и сервисами.
- Вам нужны расширенные функции, такие как расширения типов, ключевые директивы и распределенная трассировка.
- Вы хотите более надежное и масштабируемое решение для федерации.
- Вы предпочитаете более декларативный и автоматизированный подход к федерации.
Примеры из реального мира и сценарии использования
Вот несколько примеров из реального мира того, как можно использовать федерацию GraphQL, включая сшивание схем:
- Платформа электронной коммерции: Платформа электронной коммерции может использовать федерацию GraphQL для объединения данных из нескольких сервисов, таких как сервис каталога продуктов, сервис пользователей, сервис заказов и сервис платежей. Это позволяет клиентам легко получать всю необходимую информацию для отображения деталей продукта, профилей пользователей, истории заказов и информации о платежах.
- Платформа социальных сетей: Платформа социальных сетей может использовать федерацию GraphQL для объединения данных из сервисов, которые управляют профилями пользователей, постами, комментариями и лайками. Это позволяет клиентам эффективно извлекать всю информацию, необходимую для отображения профиля пользователя, его постов, а также комментариев и лайков, связанных с этими постами.
- Приложение для финансовых услуг: Приложение для финансовых услуг может использовать федерацию GraphQL для объединения данных из сервисов, которые управляют счетами, транзакциями и инвестициями. Это позволяет клиентам легко получать всю необходимую информацию для отображения балансов счетов, истории транзакций и инвестиционных портфелей.
- Система управления контентом (CMS): CMS может использовать федерацию GraphQL для интеграции данных из различных источников, таких как статьи, изображения, видео и контент, созданный пользователями. Это позволяет создать единый API для получения всего контента, связанного с определенной темой или автором.
- Приложение для здравоохранения: Интеграция данных пациентов из различных систем, таких как электронные медицинские карты (EHR), результаты лабораторных исследований и расписание приемов. Это предоставляет врачам единую точку доступа к комплексной информации о пациенте.
Лучшие практики для сшивания схем
Чтобы обеспечить успешную реализацию сшивания схем, следуйте этим лучшим практикам:
- Тщательно планируйте свою схему: Прежде чем начать сшивать схемы, тщательно спланируйте структуру унифицированной схемы. Это включает определение отношений между типами из разных схем, переименование типов и полей для избежания конфликтов и учет общих шаблонов доступа к данным.
- Используйте последовательные соглашения об именовании: Применяйте последовательные соглашения об именовании для типов, полей и операций во всех сервисах. Это поможет избежать конфликтов и облегчит понимание унифицированной схемы.
- Документируйте свою схему: Тщательно документируйте унифицированную схему, включая описания типов, полей и операций. Это облегчит разработчикам понимание и использование схемы.
- Отслеживайте производительность: Отслеживайте производительность шлюза и удаленных сервисов для выявления и устранения любых узких мест. Используйте инструменты, такие как распределенная трассировка, для отслеживания запросов по нескольким сервисам.
- Внедряйте безопасность: Внедряйте соответствующие меры безопасности для защиты шлюза и удаленных сервисов от несанкционированного доступа. Это может включать использование механизмов аутентификации и авторизации, а также проверку входных данных и кодирование выходных.
- Версионируйте свою схему: По мере развития ваших схем, версионируйте их надлежащим образом, чтобы клиенты могли продолжать использовать старые версии схемы без сбоев. Это поможет избежать ломающих изменений и обеспечит обратную совместимость.
- Автоматизируйте развертывание: Автоматизируйте развертывание шлюза и удаленных сервисов, чтобы изменения можно было развертывать быстро и надежно. Это поможет снизить риск ошибок и повысить общую гибкость системы.
Заключение
Федерация GraphQL со сшиванием схем предлагает мощный подход к созданию унифицированных API из нескольких сервисов в микросервисной архитектуре. Понимая ее основные концепции, преимущества, ограничения и техники реализации, вы можете использовать сшивание схем для упрощения доступа к данным, улучшения масштабируемости и повышения поддерживаемости. Хотя Apollo Federation стала более продвинутым решением, сшивание схем остается жизнеспособным вариантом для более простых сценариев или при интеграции существующих сервисов GraphQL. Тщательно рассмотрите свои конкретные потребности и требования, чтобы выбрать наилучший подход для вашей организации.